
//---------------------------------------------------------------------------
//                              ColUtils
//---------------------------------------------------------------------------
//  Copyright (C): Gabriel Zachmann
//---------------------------------------------------------------------------


#ifndef ColUtils_H
#define ColUtils_H
#if defined(__sgi) || defined(_WIN32)
#pragma once
#endif

//---------------------------------------------------------------------------
//  Includes
//---------------------------------------------------------------------------

#ifdef _POSIX_VERSION
#include <sys/time.h>
#endif

#include <time.h>

#include <math.h>
#include <cstdio>
#include <Collision.h>
#include <col_import_export.h>
#include <ColGeometry.h>

using namespace std;
namespace col {

//---------------------------------------------------------------------------
//  Constants
//---------------------------------------------------------------------------

/// Epsilon; all collision detection math will use this threshold
const REAL NearZero = 1E-6;

//---------------------------------------------------------------------------
//  Forward celarations
//---------------------------------------------------------------------------

struct TopoFace;

//---------------------------------------------------------------------------
//  Functions
//---------------------------------------------------------------------------

COL_EXPORTIMPORT
REAL operator *( const Point3 &pnt, const REAL vec[3] );

COL_EXPORTIMPORT
REAL   dot3( const Vector4  &vec1, const Vector4  &vec2 );

COL_EXPORTIMPORT
REAL   dot4( const Vector4  &vec1, const Vector4  &vec2 );

COL_EXPORTIMPORT
REAL   operator *( const Vector4  &vec4, const Point3 &pnt3 );

COL_EXPORTIMPORT
REAL   operator *( const Point3   &pnt3, const Vector3  &vec3 );
COL_EXPORTIMPORT
REAL   operator *( const Vector3  &vec3, const Point3   &pnt3 );
COL_EXPORTIMPORT
REAL   operator *( const Vector3  &vec3, const Vector4  &vec4 );

COL_EXPORTIMPORT
REAL   dist2( const Point3  &pnt1, const Point3  &pnt2 );

COL_EXPORTIMPORT
Point3  barycenter( const Point3  *points, const unsigned int npoints );

COL_EXPORTIMPORT
Point3  barycenter( const Pnt3Array & points );

COL_EXPORTIMPORT
Point3  barycenter( const Point3  *points,
                    const unsigned int index[], const unsigned int nindices );
COL_EXPORTIMPORT

COL_EXPORTIMPORT
Point3  barycenter( const Pnt3Array *points,
			       const unsigned int index[], const unsigned int nindices );

COL_EXPORTIMPORT
Point3 barycenter( const Pnt3Array &points, const TopoFace &face );

COL_EXPORTIMPORT
bool collinear( const Vector3  &a, const Vector3  &b );

COL_EXPORTIMPORT
Vector3 triangleNormal( const Point3 &p0, const Point3 &p1, const Point3 &p2 );

COL_EXPORTIMPORT
void dominantIndices( const Vector3 &v, unsigned int *x, unsigned int *y );

COL_EXPORTIMPORT
void dominantIndices( const Vector3 & v,
					  unsigned int *x, unsigned int *y, unsigned int *z );

COL_EXPORTIMPORT
unsigned int dominantIndex( const Vector3 & v );

COL_EXPORTIMPORT
Point3  mulM3Pnt( const Matrix4  &m, const Point3  &p );

COL_EXPORTIMPORT
bool lockToProcessor( unsigned int processor );

COL_EXPORTIMPORT
void sleep( unsigned int microseconds );

 COL_EXPORTIMPORT
REAL time( void );

COL_EXPORTIMPORT
double my_drand48( void );

COL_EXPORTIMPORT
REAL pseudo_randomf( void );

COL_EXPORTIMPORT
unsigned int pseudo_random( void );

class COL_EXPORTIMPORT NanoTimer
{
public:

	NanoTimer();
	void start( void );
    
#if defined(_WIN32)
	static bool usesHighFrequ( void );
	static double frequ( void );
	double elapsed( void ) const;
#else
    long long unsigned int getTimeStamp() const;
    long long unsigned int elapsed( void ) const;
    void setClockID( clockid_t clk );
#endif

private:

#if defined(_WIN32)    
	static bool		M_Use_High_Frequ;
	static double	M_GHz;
	static bool		M_FrequencyChecked;
	unsigned long long int m_time_stamp;
    static void checkFrequency( void );
    static long long unsigned int getTimeStamp( void );
#else
    clockid_t m_clock_id;
    struct timespec m_time_stamp;
    void autoSetClockID( void );
#endif

};


COL_EXPORTIMPORT
unsigned int sign( float & x );

COL_EXPORTIMPORT
unsigned int sign( double x );

COL_EXPORTIMPORT
unsigned int sign( int x );

// TODO: quick fix, because MS-C++ is not C99 compatible
inline REAL roundf(REAL f) { return floor(f + 0.5); }

COL_EXPORTIMPORT
bool isectCoplanarTriangles( const Vector3f & normalV,
							 const Point3f & polyVv0, const Point3f & polyVv1,
							 const Point3f & polyVv2,
							 const Point3f & polyUv0, const Point3f & polyUv1,
							 const Point3f & polyUv2 );

COL_EXPORTIMPORT
bool isectCoplanarEdges( const Point3f & v0V, const Point3f & v1V,
						 const Point3f & u0V, const Point3f & u1V,
						 unsigned int x, unsigned int y );

COL_EXPORTIMPORT
bool pointInPolygon( const Point3f &pt,
					 const Point3f *poly, unsigned int plSize,
					 unsigned int x, unsigned int y );

COL_EXPORTIMPORT
bool pointInTriangle( const Point3f &pt,
					  const Point3f &v0, const Point3f &v1, const Point3f &v2,
					  unsigned int x, unsigned int y ); 

COL_EXPORTIMPORT
void isectEdgeTriangle( const Point3 &v1, const Point3 &v2,
						const Point3 *poly, const Vector3 &normalV,
						unsigned int x, unsigned int y,
						bool *isect, bool *oneside );

//---------------------------------------------------------------------------
//  Classes
//---------------------------------------------------------------------------

class COL_EXPORTIMPORT FibRand
{
public:

	FibRand( int seed );
	unsigned int rand( void );
	unsigned int mrand( unsigned int m );
	REAL frand( void );

	/// Maximum random number plus 1 that can be returned by FibRand
	static const int M_MaxRand = (1 << 30);

private:

	void refresh(void);

	static const int M_HashConst = 618033988;
	static const int M_KK = 100;
	static const int M_LL = 37;
	static const int M_MM = M_MaxRand;
	static const int M_TT = 70;
	static const int M_BufSize = 100;

	int m_buf[M_BufSize];
	int m_current_idx;

};


//---------------------------------------------------------------------------
//  Macros
//---------------------------------------------------------------------------


/// minimum using the ?: operator
#define col_min(col_X1,col_X2) (((col_X1) < (col_X2)) ? (col_X1) : (col_X2))

/// maximum using the ?: operator
#define col_max(col_X1,col_X2) (((col_X1) > (col_X2)) ? (col_X1) : (col_X2))

/// Maximum of 3 values
#define col_max3( col_X1, col_X2, col_X3 )		\
	col_max( col_max(col_X1,col_X2), col_X3 )

/// Absolute maximum of 3 values
#define col_abs_max3( col_X1, col_X2, col_X3 )		\
	col_max3( fabsf(col_X1), fabsf(col_X2), fabsf(col_X3) )

/// Maximum component of a vector
#define col_max_v( col_V )						\
	col_max3( (col_V)[0], (col_V)[1], (col_V)[2] )

/// increase min/max so that it contains X
#define col_min_max(col_MIN,col_X,col_MAX)        \
    if ( (col_MIN) > (col_X) )                    \
        col_MIN = (col_X);                        \
    else if ( (col_MAX) < (col_X) )               \
        col_MAX = (col_X);

/// return col_XX, except it's clamped by LOW and HIGH
#define col_clamp(col_XX, col_LOW, col_HIGH)			\
    (((col_XX) < (col_LOW)) ?					\
	     (col_LOW)						\
	   : (col_XX) > (col_HIGH) ?				\
	   		(col_HIGH)				\
		  : (col_XX))

//is a single value almost zero?
#define col_near_zero(col_X)        \
    ( fabsf( col_X ) < col::NearZero )

/// Is a vector or point almost 0?
#define col_near_null( col_V )		\
	( fabsf( col_V[0] ) < col::NearZero && 	\
	  fabsf( col_V[1] ) < col::NearZero && 	\
	  fabsf( col_V[2] ) < col::NearZero       )

/// are the two REAL values almost equal?
#define col_almost_equal( col_X, col_Y ) \
	( fabsf( (col_X) - (col_Y) ) < col::NearZero )

//////////////////////////////////////////////////////////////

void print( const Vector3 &vec, FILE *file = stdout );

void print( const Vector4 &vec, FILE *file = stdout );

void print( const Point3  &pnt, FILE *file = stdout );

void print( const Matrix4 &mat, FILE *file =stdout );

COL_EXPORTIMPORT
bool equal( const Matrix4  &mat1, const Matrix4  &mat2, REAL   diff );

COL_EXPORTIMPORT
bool equal( const Point3  &p1, const Point3  &p2, REAL   diff );

COL_EXPORTIMPORT
Point3  lincomb( float  c1, const Point3  &pnt1, float  c2, const Point3  &pnt2 );

}// namespace col

#endif /* ColUtils_H */
